#ifndef _OCPP_Log_H__
#define _OCPP_Log_H__

#include "LogStream.h"

namespace OCPP
{
	class _MainExport Log
	{
	public:
        enum LogLevel
        {
            kTrace = 0,
            kDebug,
            kInfo,
            kWarn,
            kError,
            kFatal,
            kNumberOfLogLevels
        };

        Log(LogLevel level_);
        ~Log();
        static std::function<void(const char* msg, const uint64_t len)>
            & outputFunc_()
        {
            static std::function<void(const char* msg, const uint64_t len)>
                outputFunc = Log::defaultOutputFunction;
            return outputFunc;
        }
        static std::function<void()>& flushFunc_()
        {
            static std::function<void()> flushFunc = Log::defaultFlushFunction;
            return flushFunc;
        }

        static void defaultOutputFunction(const char* msg, const uint64_t len)
        {
            fwrite(msg, 1, static_cast<size_t>(len), stdout);
        }
        static void defaultFlushFunction()
        {
            fflush(stdout);
        }

        static void setOutputFunction(
            std::function<void(const char* msg, const uint64_t len)> outputFunc,
            std::function<void()> flushFunc)
        {
            outputFunc_() = outputFunc;
            flushFunc_() = flushFunc;
        }

        LogStream& stream()
        {
            return _logStream;
        }

        static LogLevel& logLevel()
        {
            return _logLevel;
        }
    private:
        static LogLevel _logLevel;
        LogStream _logStream;
	};
}

#define OCPP_LOG_IF_(cond) for (int _r = 0; _r == 0 && (cond); _r = 1)

#define OCPP_LOG_INFO \
    OCPP_LOG_IF_(OCPP::Log::logLevel() <= OCPP::Log::kInfo) \
        OCPP::Log(OCPP::Log::kInfo).stream()
#define OCPP_LOG_DEBUG \
     OCPP_LOG_IF_(OCPP::Log::logLevel() <= OCPP::Log::kDebug) \
        OCPP::Log(OCPP::Log::kDebug).stream()
#define OCPP_LOG_ERROR \
     OCPP_LOG_IF_(OCPP::Log::logLevel() <= OCPP::Log::kError) \
        OCPP::Log(OCPP::Log::kError).stream()
#define OCPP_LOG_TRACE \
     OCPP_LOG_IF_(OCPP::Log::logLevel() <= OCPP::Log::kTrace) \
        OCPP::Log(OCPP::Log::kTrace).stream()
#endif